from fastapi import HTTPException, status
from fastapi_pagination.ext.tortoise import paginate
from redis import Redis
from tortoise.transactions import atomic

from api.v1.admin_user.schemas import CreateRole, Register, UpdateUserInfo, Permission
from api.v1.models import UserInDB, RoleInDB, PermissionInDB
from config import settings
from utils.password import password_hash
from utils.toolkit import convert_to_datetime


@atomic()
async def create_admin():
    admin_user = {
        "username": settings.DEFAULT_ADMIN_USERNAME,
        "password": password_hash(settings.DEFAULT_ADMIN_PASSWORD),
        "is_frozen": False,
        "is_admin": True
    }

    db_role = {"name": "admin"}
    db_permission = {"code": "1", "description": "访问全部接口"}
    # noinspection PyBroadException
    try:
        db_user: UserInDB = await UserInDB.get(username=settings.DEFAULT_ADMIN_USERNAME)
    except:
        db_user: UserInDB = await UserInDB.create(**admin_user)
        db_role: RoleInDB = await RoleInDB.create(**db_role)
        db_permission: PermissionInDB = await PermissionInDB.create(**db_permission)
        await db_user.roles.add(db_role)
        await db_role.permissions.add(db_permission)

    return db_user


async def create_user(register: Register) -> None:
    """
    创建新用户
    :param register:
    :return:
    """
    db_user: UserInDB = await UserInDB.filter(username=register.username).first()
    if db_user is not None:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="用户名已存在"
        )
    db_user = await UserInDB.create(
        username=register.username,
        password=password_hash(register.password),
        avatar=register.avatar,
        is_frozen=register.is_frozen,
        is_admin=register.is_admin,
    )
    role_ids = [k.id for k in register.role]
    db_roles = await RoleInDB.filter(id__in=role_ids).all()
    await db_user.roles.add(*db_roles)

    return None


async def create_role(createRole: CreateRole) -> None:
    """
    创建角色
    :param createRole:
    :return:
    """
    db_role = await RoleInDB.filter(name=createRole.name).first()
    if db_role is None:
        await RoleInDB.create(name=createRole.name)
    else:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="角色已存在"
        )
    return None


@atomic()
async def delete_role(id: int) -> None:
    """
    删除角色
    :param id:
    :return:
    """
    db_role = await RoleInDB.filter(id=id).first().prefetch_related("permissions", "users")
    if db_role is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="角色未找到"
        )
    await db_role.permissions.clear()
    await db_role.users.clear()
    await db_role.delete()
    return None


async def user_list(username, start_time, end_time, is_frozen, is_admin):
    """
    获取用户列表
    :param username:
    :param start_time:
    :param end_time:
    :param is_frozen:
    :param is_admin:
    :return:
    """
    data = {}
    if username:
        data["username__icontains"] = username
    if start_time:
        start_time = convert_to_datetime(start_time)
        data["create_at__gte"] = start_time
    if end_time:
        end_time = convert_to_datetime(end_time)
        data["create_at__lte"] = end_time
    if is_frozen is not None:
        data["is_frozen"] = is_frozen
    if is_admin is not None:
        data["is_admin"] = is_admin

    # paginate中需要放入的是一个Tortoise ORM 的查询集
    return await paginate(UserInDB.filter(**data))


async def user_info(id: int):
    """
    获取用户信息
    :param id:
    :return:
    """
    db_user = await UserInDB.filter(id=id).first().prefetch_related("roles__permissions")
    if db_user is None:
        raise HTTPException(
            status_code=404,
            detail="用户未找到"
        )

    roles = []
    permissions = []
    for role in db_user.roles:
        roles.append(role.name)
        for permission in role.permissions:
            permissions.append(permission.__dict__)
    db_user.role = roles
    db_user.permissions = permissions
    return db_user


@atomic()
async def delete_user(id: int):
    """
    删除用户
    :param id:
    :return:
    """
    db_user = await UserInDB.filter(id=id).first().prefetch_related("roles__permissions")
    if db_user is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="用户未找到"
        )
    await db_user.roles.clear()
    await db_user.delete()
    return None


@atomic()
async def update_user_info(id: int, user_info: UpdateUserInfo):
    """
    更新用户信息
    :param id:
    :param user_info:
    :return:
    """
    db_user = await UserInDB.filter(id=id).first().prefetch_related("roles__permissions")
    if db_user is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="用户未找到"
        )
    if user_info.username:
        db_user.username = user_info.username
    if user_info.avatar:
        db_user.avatar = user_info.avatar
    if user_info.is_admin:
        db_user.avatar = user_info.is_admin
    if user_info.is_frozen:
        db_user.avatar = user_info.is_frozen
    if user_info.password:
        db_user.avatar = user_info.password
    if user_info.role:
        roles = []
        for k in user_info.role:
            roles.append(k.id)
        db_role = await RoleInDB.filter(id__in=roles)
        await db_user.roles.add(*db_role)
    await db_user.save()
    return None


async def create_permissions(permissions: Permission):
    """
    创建权限
    :param permissions:
    :return:
    """
    db_permission = await PermissionInDB.filter(code=permissions.code).first()

    if db_permission is not None:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="权限码重复"
        )

    await PermissionInDB.create(
        code=permissions.code,
        description=permissions.description
    )

    return None


@atomic()
async def roles_join_permissions(role_id: int, permissions_id: int):
    """
    为角色添加权限
    :param role_id:
    :param permissions_id:
    :return:
    """
    db_role = await RoleInDB.filter(id=role_id).first()
    db_permission = await PermissionInDB.filter(id=permissions_id).first()
    if db_role is None or db_permission is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="角色/权限不存在"
        )
    await db_role.permissions.add(db_permission)
    return None


async def reset_password(user_id: int):
    """
    重置密码
    :param user_id:
    :return:
    """
    db_user = await UserInDB.filter(id=user_id).first()
    if db_user is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="用户未找到"
        )
    db_user.password = password_hash(settings.DEFAULT_PASSWORD)
    await db_user.save()
    return None


async def role_list(name):
    """
    获取角色列表，模糊查询
    :param name:
    :return:
    """
    data = {}
    if name:
        data["name__icontains"] = name

    return await paginate(RoleInDB.filter(**data))


async def permissions_list(description, code):
    """
    获取权限列表，模糊查询
    :param description:
    :param code:
    :return:
    """
    data = {}
    if description:
        data["description__icontains"] = description
    if code:
        data["code"] = code
    return await paginate(PermissionInDB.filter(**data))


@atomic()
async def delete_permissions(id: int):
    """
    删除权限
    :param id:
    :return:
    """
    db_permission = await PermissionInDB.filter(id=id).first()
    if db_permission is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="权限未找到"
        )
    await db_permission.roles.clear()
    await db_permission.delete()
    return None


async def redis_test_demo(key: str, value: str, redis: Redis):
    """
    测试Redis连接
    :param value:
    :param key:
    :param redis:
    :return:
    """
    try:
        await redis.set(name=key, value=value)
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Redis数据库连接异常：{str(e)}"
        )
    return None
